"""
集成测试脚本

测试视频标注系统的完整功能，包括：
- 批量视频处理
- 结果文件格式验证
- 关键词搜索功能
- 错误场景处理
"""

import os
import shutil
from pathlib import Path
import cv2
import numpy as np
from video_annotation_middleware import VideoAnnotationMiddleware, CMatchVideo


class IntegrationTest:
    """集成测试类"""
    
    def __init__(self):
        """初始化测试环境"""
        self.test_videos_dir = "test_videos"
        self.test_results_dir = "test_results"
        self.middleware = VideoAnnotationMiddleware(
            videos_dir=self.test_videos_dir,
            results_dir=self.test_results_dir
        )
        self.test_passed = 0
        self.test_failed = 0
    
    def setup(self):
        """准备测试环境"""
        print("=" * 60)
        print("准备测试环境...")
        print("=" * 60)
        
        # 创建测试目录
        Path(self.test_videos_dir).mkdir(exist_ok=True)
        Path(self.test_results_dir).mkdir(exist_ok=True)
        
        # 生成测试视频文件
        self._create_test_videos()
        
        print("✓ 测试环境准备完成\n")
    
    def teardown(self):
        """清理测试环境"""
        print("\n" + "=" * 60)
        print("清理测试环境...")
        print("=" * 60)
        
        # 删除测试目录
        if Path(self.test_videos_dir).exists():
            shutil.rmtree(self.test_videos_dir)
            print(f"✓ 已删除测试视频目录: {self.test_videos_dir}")
        
        if Path(self.test_results_dir).exists():
            shutil.rmtree(self.test_results_dir)
            print(f"✓ 已删除测试结果目录: {self.test_results_dir}")
    
    def _create_test_videos(self):
        """创建测试视频文件"""
        print("\n创建测试视频文件...")
        
        # 创建3个简单的测试视频
        video_configs = [
            ("test_video1.mp4", 30, (640, 480)),
            ("test_video2.avi", 20, (320, 240)),
            ("test_video3.mov", 15, (640, 480))
        ]
        
        for video_name, num_frames, resolution in video_configs:
            video_path = Path(self.test_videos_dir) / video_name
            self._create_simple_video(str(video_path), num_frames, resolution)
            print(f"  ✓ 创建测试视频: {video_name} ({num_frames}帧, {resolution})")
    
    def _create_simple_video(self, output_path: str, num_frames: int, resolution: tuple):
        """
        创建简单的测试视频
        
        参数:
            output_path: 输出视频路径
            num_frames: 帧数
            resolution: 分辨率 (width, height)
        """
        fourcc = cv2.VideoWriter_fourcc(*'mp4v')
        fps = 10
        out = cv2.VideoWriter(output_path, fourcc, fps, resolution)
        
        for i in range(num_frames):
            # 创建彩色帧（渐变效果）
            frame = np.zeros((resolution[1], resolution[0], 3), dtype=np.uint8)
            color_value = int((i / num_frames) * 255)
            frame[:, :] = [color_value, 255 - color_value, 128]
            
            # 添加帧号文本
            cv2.putText(frame, f"Frame {i+1}", (50, 50), 
                       cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 255, 255), 2)
            
            out.write(frame)
        
        out.release()
    
    def _create_invalid_video(self):
        """创建无效的视频文件用于错误测试"""
        invalid_path = Path(self.test_videos_dir) / "invalid_video.mp4"
        # 创建一个空文件或损坏的文件
        with open(invalid_path, 'w') as f:
            f.write("This is not a valid video file")
        print(f"  ✓ 创建无效视频文件: invalid_video.mp4")
    
    def run_test(self, test_name: str, test_func):
        """
        运行单个测试
        
        参数:
            test_name: 测试名称
            test_func: 测试函数
        """
        print("\n" + "-" * 60)
        print(f"测试: {test_name}")
        print("-" * 60)
        
        try:
            result = test_func()
            if result:
                print(f"✓ 测试通过: {test_name}")
                self.test_passed += 1
            else:
                print(f"✗ 测试失败: {test_name}")
                self.test_failed += 1
        except Exception as e:
            print(f"✗ 测试异常: {test_name}")
            print(f"  错误信息: {str(e)}")
            import traceback
            traceback.print_exc()
            self.test_failed += 1
    
    def test_load_model(self):
        """测试1: 加载YOLO模型"""
        print("加载YOLO模型...")
        self.middleware.loadModel()
        
        # 验证模型已加载
        if self.middleware._model is None:
            print("✗ 模型未成功加载")
            return False
        
        print("✓ YOLO模型加载成功")
        return True
    
    def test_process_single_video(self):
        """测试2: 处理单个视频"""
        video_path = Path(self.test_videos_dir) / "test_video1.mp4"
        
        print(f"处理视频: {video_path}")
        
        # 定义进度回调
        progress_info = {'current': 0, 'total': 0}
        def callback(current, total):
            progress_info['current'] = current
            progress_info['total'] = total
        
        # 处理视频
        success = self.middleware.processVideo(str(video_path), callback=callback)
        
        if not success:
            print("✗ 视频处理失败")
            return False
        
        print(f"✓ 视频处理成功 (处理了 {progress_info['current']}/{progress_info['total']} 帧)")
        
        # 验证结果文件是否生成
        result_file = Path(self.test_results_dir) / "test_video1.txt"
        if not result_file.exists():
            print(f"✗ 结果文件未生成: {result_file}")
            return False
        
        print(f"✓ 结果文件已生成: {result_file}")
        return True
    
    def test_batch_processing(self):
        """测试3: 批量处理视频"""
        print("批量处理所有测试视频...")
        
        # 定义进度回调
        processed_videos = []
        def callback(current, total, video_name):
            processed_videos.append(video_name)
            print(f"  处理进度: {current}/{total} - {video_name}")
        
        # 批量处理
        result = self.middleware.processVideosFromPath(callback=callback)
        
        print(f"\n批量处理结果:")
        print(f"  总视频数: {result['total']}")
        print(f"  成功处理: {result['success']}")
        print(f"  处理失败: {len(result['failed'])}")
        
        if result['failed']:
            print(f"  失败列表: {result['failed']}")
        
        # 验证至少处理了一些视频
        if result['success'] == 0:
            print("✗ 没有成功处理任何视频")
            return False
        
        print(f"✓ 成功处理 {result['success']} 个视频")
        return True
    
    def test_result_file_format(self):
        """测试4: 验证结果文件格式"""
        result_file = Path(self.test_results_dir) / "test_video1.txt"
        
        if not result_file.exists():
            print(f"✗ 结果文件不存在: {result_file}")
            return False
        
        print(f"验证结果文件格式: {result_file}")
        
        # 读取结果文件
        with open(result_file, 'r', encoding='utf-8') as f:
            lines = f.readlines()
        
        if not lines:
            print("✗ 结果文件为空")
            return False
        
        print(f"  文件包含 {len(lines)} 行")
        
        # 验证格式：每行应该是 "Frame X: ..." 格式
        valid_format = True
        for i, line in enumerate(lines[:5], 1):  # 检查前5行
            line = line.strip()
            if not line.startswith("Frame "):
                print(f"✗ 第{i}行格式不正确: {line}")
                valid_format = False
                break
            print(f"  第{i}行: {line[:60]}...")  # 显示前60个字符
        
        if not valid_format:
            return False
        
        print("✓ 结果文件格式正确")
        return True
    
    def test_keyword_search(self):
        """测试5: 关键词搜索功能"""
        # 首先确保有结果文件
        result_files = list(Path(self.test_results_dir).glob("*.txt"))
        if not result_files:
            print("✗ 没有结果文件可供搜索")
            return False
        
        print(f"在 {len(result_files)} 个结果文件中搜索关键词...")
        
        # 测试搜索常见对象（YOLO可能检测到的对象）
        test_keywords = ["Frame", "person", "car", "dog", "cat"]
        
        search_results = {}
        for keyword in test_keywords:
            print(f"\n  搜索关键词: '{keyword}'")
            results = self.middleware.searchKeyword(keyword)
            search_results[keyword] = results
            
            if results:
                print(f"    ✓ 找到 {len(results)} 个匹配的视频")
                for match in results[:2]:  # 显示前2个结果
                    print(f"      - {match.m_videoName}: {match.m_matchCnt} 次匹配")
            else:
                print(f"    - 未找到匹配结果")
        
        # 验证至少有一个关键词有结果（Frame应该总是存在）
        if not any(search_results.values()):
            print("\n✗ 所有关键词搜索都没有结果")
            return False
        
        print("\n✓ 关键词搜索功能正常")
        return True
    
    def test_case_insensitive_search(self):
        """测试6: 不区分大小写的搜索"""
        print("测试不区分大小写的搜索...")
        
        # 搜索相同关键词的不同大小写形式
        keyword_lower = "frame"
        keyword_upper = "FRAME"
        keyword_mixed = "Frame"
        
        results_lower = self.middleware.searchKeyword(keyword_lower)
        results_upper = self.middleware.searchKeyword(keyword_upper)
        results_mixed = self.middleware.searchKeyword(keyword_mixed)
        
        print(f"  '{keyword_lower}' 搜索结果: {len(results_lower)} 个")
        print(f"  '{keyword_upper}' 搜索结果: {len(results_upper)} 个")
        print(f"  '{keyword_mixed}' 搜索结果: {len(results_mixed)} 个")
        
        # 验证结果数量相同
        if len(results_lower) != len(results_upper) or len(results_lower) != len(results_mixed):
            print("✗ 不同大小写的搜索结果数量不一致")
            return False
        
        print("✓ 不区分大小写搜索功能正常")
        return True
    
    def test_invalid_video_handling(self):
        """测试7: 处理无效视频文件"""
        print("测试无效视频文件处理...")
        
        # 创建无效视频文件
        self._create_invalid_video()
        
        invalid_video_path = Path(self.test_videos_dir) / "invalid_video.mp4"
        
        # 尝试处理无效视频
        success = self.middleware.processVideo(str(invalid_video_path))
        
        # 应该返回False，表示处理失败
        if success:
            print("✗ 无效视频应该处理失败，但返回了成功")
            return False
        
        print("✓ 无效视频文件被正确处理（返回失败）")
        return True
    
    def test_empty_directory(self):
        """测试8: 处理空目录"""
        print("测试空目录处理...")
        
        # 创建一个空的视频目录
        empty_dir = "empty_videos"
        Path(empty_dir).mkdir(exist_ok=True)
        
        try:
            # 创建使用空目录的中间件
            empty_middleware = VideoAnnotationMiddleware(videos_dir=empty_dir)
            empty_middleware.loadModel()
            
            # 处理空目录
            result = empty_middleware.processVideosFromPath()
            
            print(f"  处理结果: {result}")
            
            # 验证结果
            if result['total'] != 0:
                print(f"✗ 空目录应该返回0个视频，但返回了 {result['total']}")
                return False
            
            print("✓ 空目录被正确处理")
            return True
            
        finally:
            # 清理空目录
            if Path(empty_dir).exists():
                shutil.rmtree(empty_dir)
    
    def test_nonexistent_directory(self):
        """测试9: 处理不存在的目录"""
        print("测试不存在的目录...")
        
        # 创建使用不存在目录的中间件
        nonexistent_middleware = VideoAnnotationMiddleware(videos_dir="nonexistent_dir")
        
        try:
            # 尝试处理不存在的目录
            result = nonexistent_middleware.processVideosFromPath()
            print("✗ 应该抛出异常，但没有抛出")
            return False
        except FileNotFoundError as e:
            print(f"  ✓ 正确抛出FileNotFoundError: {str(e)}")
            return True
        except Exception as e:
            print(f"✗ 抛出了错误的异常类型: {type(e).__name__}")
            return False
    
    def test_search_results_sorting(self):
        """测试10: 验证搜索结果按匹配次数排序"""
        print("测试搜索结果排序...")
        
        # 搜索一个常见的关键词
        results = self.middleware.searchKeyword("Frame")
        
        if not results:
            print("  - 没有搜索结果，跳过排序测试")
            return True
        
        print(f"  找到 {len(results)} 个结果")
        
        # 验证结果按m_matchCnt降序排序
        is_sorted = True
        for i in range(len(results) - 1):
            if results[i].m_matchCnt < results[i + 1].m_matchCnt:
                print(f"✗ 结果未正确排序: 位置{i}的匹配数({results[i].m_matchCnt}) < 位置{i+1}的匹配数({results[i+1].m_matchCnt})")
                is_sorted = False
                break
            print(f"  结果{i+1}: {results[i].m_videoName} - {results[i].m_matchCnt} 次匹配")
        
        if not is_sorted:
            return False
        
        print("✓ 搜索结果正确排序")
        return True
    
    def print_summary(self):
        """打印测试总结"""
        print("\n" + "=" * 60)
        print("测试总结")
        print("=" * 60)
        print(f"总测试数: {self.test_passed + self.test_failed}")
        print(f"通过: {self.test_passed}")
        print(f"失败: {self.test_failed}")
        
        if self.test_failed == 0:
            print("\n🎉 所有测试通过！")
        else:
            print(f"\n⚠️  有 {self.test_failed} 个测试失败")
        
        print("=" * 60)
    
    def run_all_tests(self):
        """运行所有集成测试"""
        print("\n" + "=" * 60)
        print("开始集成测试")
        print("=" * 60)
        
        # 准备测试环境
        self.setup()
        
        try:
            # 运行所有测试
            self.run_test("1. 加载YOLO模型", self.test_load_model)
            self.run_test("2. 处理单个视频", self.test_process_single_video)
            self.run_test("3. 批量处理视频", self.test_batch_processing)
            self.run_test("4. 验证结果文件格式", self.test_result_file_format)
            self.run_test("5. 关键词搜索功能", self.test_keyword_search)
            self.run_test("6. 不区分大小写搜索", self.test_case_insensitive_search)
            self.run_test("7. 处理无效视频文件", self.test_invalid_video_handling)
            self.run_test("8. 处理空目录", self.test_empty_directory)
            self.run_test("9. 处理不存在的目录", self.test_nonexistent_directory)
            self.run_test("10. 验证搜索结果排序", self.test_search_results_sorting)
            
        finally:
            # 清理测试环境
            self.teardown()
        
        # 打印测试总结
        self.print_summary()
        
        return self.test_failed == 0


def main():
    """主函数"""
    test = IntegrationTest()
    success = test.run_all_tests()
    
    # 返回退出码
    exit(0 if success else 1)


if __name__ == "__main__":
    main()
